import ms = require("ms");
import {DeliveryModel} from "../database/models/delivery";
import {DeliveryOrderModel} from "../database/models/deliveryOrder";
import {GameActivityModel} from "../database/models/gameActivity";
import {GamePrizeModel} from "../database/models/gamePrize";
import {LipstickGameOrder, LipstickGameOrderModel} from "../database/models/lipstickGameOrder";
import {LipstickSeries, LipstickSeriesModel} from "../database/models/lipstickSeries";
import {GiftState, MailModel, MailState, MailType} from "../database/models/mail";
import {Player, PlayerModel} from "../database/models/player";
import {PressGameOrder, PressGameOrderModel} from "../database/models/pressGameOrder";
import {PressGroup, PressGroupModel} from "../database/models/ProductGroup";
import {ProductOrderModel} from "../database/models/ProductOrder";
import {RechargeOrderModel} from "../database/models/rechargeOrder";
import {InternalError} from "./error";
import {
  LIPSTICK_GAME_REVIVE_TIME_0,
  LIPSTICK_GAME_REVIVE_TIME_1,
  LIPSTICK_GAME_REVIVE_TIME_2,
  LipStickGame,
  playerUseCoinOrGemOrPoint
} from "./games/lipStickGame";
import {PressGame} from "./games/pressGame";
import {kickBackAll} from "./utils";

type ORDER_TYPE = 'ProductOrder' | 'RechargeOrder' | 'DeliveryOrder' | 'LipstickGameOrder' | 'PressGameOrder'

interface IPaymentHandler {
  handleOrderType(): ORDER_TYPE

  handle(order: string): Promise<void>
}

export class PaymentDispatcher {

  private handlerMap: { [key: string]: IPaymentHandler } = {}

  register(handler: IPaymentHandler) {
    this.handlerMap[handler.handleOrderType()] = handler
  }

  async confirm(payment: { orderType: string, order: string }) {

    const handler = this.handlerMap[payment.orderType]

    if (handler) {
      await handler.handle(payment.order)
    } else {
      throw Error(`UNKNOW_ORDER_TYPE_${payment.orderType}`)
    }
  }
}

class ProductOrderHandler implements IPaymentHandler {

  handleOrderType = (): 'ProductOrder' => 'ProductOrder'

  async handle(order: string) {

    const productOrder = await ProductOrderModel.findById(order)

    productOrder.state = 'paid'
    await productOrder.save()
    const po = await ProductOrderModel.findById(order).populate(`product`).lean()

    const prize = await GamePrizeModel.create({
      player: productOrder.player,
      product: productOrder.product,
      productName: productOrder.productName,
      productModel: po.product.model,
      productDescription: productOrder.productDescription,
      productUrl: productOrder.productCoverUrl,
      state: 'idle',
      from: 'buy',
      createAt: new Date(),
      selectState: `notAllowed`
    })
    await MailModel.create({
      title: `购买成功通知`,
      content: `${prize.productName}已成功购买`,
      to: prize.player,
      type: MailType.NOTICE,
      gift: {prize: prize._id},
      giftState: GiftState.NOTALLOW,
      state: MailState.UNREAD
    })
    await kickBackAll(productOrder._id, `buy`)
  }
}

class RechargeOrderHandler implements IPaymentHandler {

  handleOrderType = (): 'RechargeOrder' => 'RechargeOrder'

  async handle(order: string) {

    const rechargeOrder = await RechargeOrderModel.findById(order)

    rechargeOrder.state = 'paid'
    rechargeOrder.finishAt = new Date()
    await rechargeOrder.save()

    const updated = await PlayerModel.findByIdAndUpdate(
      rechargeOrder.player,
      {$inc: {coin: rechargeOrder.coin, freeCoin: rechargeOrder.freeCoin, gem: rechargeOrder.gem}},
      {new: true}
    )
    await kickBackAll(rechargeOrder._id, `recharge`)
  }
}

class DeliveryOrderHandler implements IPaymentHandler {

  handleOrderType = (): 'DeliveryOrder' => 'DeliveryOrder'

  async handle(order: string) {

    const deliveryOrder = await DeliveryOrderModel
      .findByIdAndUpdate(order, {$set: {state: 'paid'}}, {new: true})
      .populate(`prizes`).lean()

    for (const iterator of deliveryOrder.prizes) {
      await GamePrizeModel.findOneAndUpdate({
          _id: iterator._id,
          player: deliveryOrder.player._id
        },
        {
          $set: {
            state: 'prepared',
            requestDeliveryAt: new Date(),
            toAddress: deliveryOrder.toAddress
          }
        })
    }

    const delivery = await DeliveryModel.create({
      player: deliveryOrder.player,
      prizes: deliveryOrder.prizes,
      toAddress: deliveryOrder.toAddress,
      fee: deliveryOrder.RMBCost,
      state: `havePaid`
    })
  }
}

class LipstickGameOrderHandler implements IPaymentHandler {

  handleOrderType = (): 'LipstickGameOrder' => 'LipstickGameOrder'

  async handle(order: string) {
    console.log(`${__filename} LipstickGameOrderHandler RMB开始口红机接口已经移除`)
    return

    // @ts-ignore
    const lipstickGameOrder: InstanceType<LipstickGameOrder> = await LipstickGameOrderModel.findById(order)
    lipstickGameOrder.state = 'paid'

    // @ts-ignore
    const player: InstanceType<Player> = await PlayerModel.findById(lipstickGameOrder.player)
    // @ts-ignore
    const lipstickSeries: InstanceType<LipstickSeries> =
      await LipstickSeriesModel.findById(lipstickGameOrder.lipstickSeries)

    const useCoin = lipstickSeries.priceInCoin

    const updated = await PlayerModel.findByIdAndUpdate(
      lipstickGameOrder.player,
      {$inc: {coin: useCoin}},
      {new: true})

    if (lipstickGameOrder.payFor === `resume`) {
      const {ok} = await playerUseCoinOrGemOrPoint(player, useCoin, lipstickGameOrder.activity, 'resumeActivity')
      if (!ok) {
        console.log(`NO_ENOUGH_COIN when pay to resume`)
        return
      }
      const act = await GameActivityModel.findById(lipstickGameOrder.activity)
      if (!act)
        throw InternalError("ALREADY_CLAIMED")

      let endAt = null
      switch (act.stage) {
        case 0:
          endAt = new Date(Date.now() + ms(`${LIPSTICK_GAME_REVIVE_TIME_0}s`))
          break
        case 1:
          endAt = new Date(Date.now() + ms(`${LIPSTICK_GAME_REVIVE_TIME_1}s`))
          break
        case 2:
          endAt = new Date(Date.now() + ms(`${LIPSTICK_GAME_REVIVE_TIME_2}s`))
          break
        default:
          endAt = new Date(Date.now() + ms(`1s`))
      }
      act.state = `free`
      act.endAt = endAt
      act.resumeTimes++
      await act.save()
    } else if (lipstickGameOrder.payFor === `start`) {
      const game = new LipStickGame(
        player,
        lipstickSeries)
      const activity = await game.start()
      lipstickGameOrder.activity = activity
    }
    await lipstickGameOrder.save()
  }
}

class PressGameOrderHandler implements IPaymentHandler {

  handleOrderType = (): 'PressGameOrder' => 'PressGameOrder'

  async handle(order: string) {
    console.log(`${__filename} PressGameOrderHandler RMB开始按的准接口已经移除`)
    return

    // @ts-ignore
    const pressGameOrder: InstanceType<PressGameOrder>
      = await PressGameOrderModel.findById(order)
    pressGameOrder.state = 'paid'

    // @ts-ignore
    const player: InstanceType<Player> = await PlayerModel.findById(pressGameOrder.player)
    // @ts-ignore
    const pressGroup: InstanceType<PressGroup> =
      await PressGroupModel.findById(pressGameOrder.pressGroup)

    const useCoin = pressGroup.priceInCoin

    const updated = await PlayerModel.findByIdAndUpdate(
      pressGameOrder.player,
      {$inc: {coin: useCoin}},
      {new: true})

    const game = new PressGame(
      player,
      pressGroup)
    const activity = await game.start()
    pressGameOrder.activity = activity

    await pressGameOrder.save()
  }
}

export const paymentDispatcher = new PaymentDispatcher()

paymentDispatcher.register(new ProductOrderHandler())
paymentDispatcher.register(new RechargeOrderHandler())
paymentDispatcher.register(new DeliveryOrderHandler())
paymentDispatcher.register(new LipstickGameOrderHandler())
paymentDispatcher.register(new PressGameOrderHandler())
